import random
import numpy

class Player:
    def __init__(self):
        # keeps track of hunt decisionts for hunt_outcomes
        self.decision_list = 0 

        # dictionary for determining whether other player hunted or not
        self.hunt_calc = {"h0" : True, "h-3" : False, "s1": True, "s-2": False}

        # list to keep track of 'your reputation vs chance of them hunting'
        self.reputation_count = []

        # list to keep track of m vs total hunts
        self.total_hunts_count = []

        # keeps track of your own reputation (for use in other functions)
        self.reputation = 0

        # keeps track of reputation of opponents (for use in other functions)
        self.reputation_list = 0

        # keeps track of round number (for use in other functions)
        self.round_number = 0

        # the initial population of tribe
        self.initial_population = 0

        # integral of population
        self.integral_population = 0

    # round number: int
    # current_food: int
    # current_reputation: float
    # m: int
    # player_reputations: list of floats
    def hunt_choices(self, round_number, current_food, current_reputation, m,
            player_reputations):
        self.reputation_list = player_reputations
        self.reputation = current_reputation
        self.round_number = round_number
        self.integral_population = self.integral_population + len(player_reputations)

        hunt_decisions = ['s' for x in player_reputations]

        # to hunt is a list of (indices of) people that we will be hunting with
        to_hunt = []
        if (round_number == 1):
            # first round, always hunt. the scenario of 1st round
            # always hunt, 2nd round never hunt
            # will probably end up better than 1st round never hunt
            # 2nd round always hunt
            self.initial_population = len(player_reputations)
            to_hunt = self.decide_hunt(len(player_reputations))
        elif (round_number == 2):
            # 2nd round, get to the average number of hunts
            avg = self.total_hunts_count[0][1]
            to_hunt = self.decide_hunt(self.num_hunts_to_target(avg))
        elif (round_number < 21):
            # take a some random data for some rounds
            avg = self.total_hunts_count[round_number - 2][1]
            rand = avg + ((random.random() - 1.0) / 8)
            to_hunt = self.decide_hunt(self.num_hunts_to_target(rand))
        elif (len(self.reputation_list) < self.initial_population / 20):
            # if population is 5% of original population just never hunt 
            # small enough that reputation loses value
            to_hunt = []
        else:
            # most of the game, calculate optimal reputation and
            # choose the n highest players to get to that reputation
            x_values = []
            y_values = []
            for x in range(len(reputation_count) - 21, len(reputation_count) - 1):
                x_values.append(reputation_count[x][0])
                y_values.append(reputation_count[x][1])
            # fitting polynomial to last 20 data points
            coeff = numpy.polyfit(x_values, y_values, 2)

            # maximum reputation change
            max_rep_change = (self.integral_population * current_reputation + len(reputation_count)) / (self.integral_population + len(reputation_count))

            if (coeff[0] < 0):
                # model has a local max

                # target reputation to aim for
                target = (1 - 3 * coeff[1]) / (6 * coeff[0])

                # with a little random (for future regressions)
                target = target + (random.random() - 1) * max_rep_change / 3

                to_hunt = self.decide_hunt(self.num_hunts_to_target(target))
            elif (coeff[0] == 0):
                # model is linear
                if (3 * coeff[1] - 1 > 0):
                    to_hunt = self.decide_hunt(len(player_reputations))
                elif (3 * coeff[1] - 1 < 0):
                    to_hunt = self.decide_hunt(0)
            else:
                # model has a local min
                to_hunt = self.decide_hunt(0)
            
        for item in to_hunt:
            hunt_decisions[item] = 'h'



        self.decision_list = hunt_decisions
        return hunt_decisions

    # food_earnings: list of ints
    def hunt_outcomes(self, food_earnings):
        self.calculate_num_hunts(food_earnings)

    # award: int
    # m: int
    # number_hunters: int
    def round_end(self, award, m, number_hunters):
        self.total_hunts_count.append((m, number_hunters / len(self.reputation_list)))

    # calculates what fraction of people hunted with you and adds it to reputation_count
    def calculate_num_hunts(self, food_earnings):
        hunts = 0
        total = 0
        for x in range(len(self.decision_list)):
            value = str(self.decision_list[x]) + str(food_earnings[x])
            result = self.hunt_calc.get(value, False)
            if (result):
                hunts = hunts + 1
                total = total + 1
            else:
                total = total + 1
        self.reputation_count.append((self.reputation, hunts / total))

    # returns a list of the indices of people we will be hunting with
    def decide_hunt(self, num_hunts):
        new_list = sorted(self.reputation_list, reverse = True)
        ret = []
        for x in range(int(num_hunts)):
            ret.append(self.find_player(new_list[x], ret))
        return ret

    # returns index of player with certain reputation
    def find_player(self, reputation, cur_list):
        for x in range(len(self.reputation_list)):
            if (self.reputation_list[x] == reputation):
                if (not (x in cur_list)):
                    return x
        return 0

    # returns the number of hunts required to get to target_reputation
    def num_hunts_to_target(self, target_reputation):
        ret = target_reputation * (self.integral_population + len(self.reputation_list)) - (self.integral_population * self.reputation)
        if (ret > len(self.reputation_list)):
            ret = len(self.reputation_list)
        elif (ret < 0):
            ret = 0
        return ret